/**
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

package pittypat.data;

import java.beans.PropertyVetoException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.HashMap;

import pittypat.FunctionArgs;
import pittypat.FunctionModel;
import pittypat.IAsyncProgress;
import pittypat.Module;
import pittypat.configuration.AppConfig;
import pittypat.configuration.ConfigException;
import pittypat.configuration.ModuleConfig;
import pittypat.configuration.PittypatConfig;

/**
 * IDataService 的抽象实现。
 */
public abstract class DataService implements IDataService {
	/**
	 * 初始化 DataService 类的新实例。
	 */
	protected DataService() {
	}

	/**
	 * 在当前数据服务上执行指定的功能数据操作。
	 * 
	 * @param model
	 *            功能模型对象，其中包含了功能的参数列表、执行状态以及相关消息。
	 * @param progress
	 *            跟踪和汇报异步操作的进度。
	 * @throws ConfigException
	 * @throws SQLException
	 * @throws UnsupportedEncodingException
	 * @throws PropertyVetoException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 */
	@Override
	public abstract <T extends FunctionArgs> void exec(FunctionModel<T> model, IAsyncProgress progress)
			throws ConfigException, SQLException, UnsupportedEncodingException, PropertyVetoException,
			InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException,
			NoSuchMethodException, SecurityException;

	private static final HashMap<Class<?>, IDataService> cachedDataServices = new HashMap<Class<?>, IDataService>();
	private static final Object CACHED_DATA_SERVICES_LOCK = new Object();

	/**
	 * 为指定类型的 Module
	 * 类型获取或创建一个新的数据服务对象，如果此前曾经创建过该类型的数据服务，则返回缓存的数据服务，否则创建新的对象实例并缓存。
	 * 
	 * @param apiType
	 *            抽象类型的 Api 类型。
	 * @return 可以用于在目标类型模块中进行数据操作的 IDataService 对象实例。
	 * @throws ConfigException
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 */
	public static IDataService find(Class<?> apiType)
			throws ConfigException, ClassNotFoundException, InstantiationException, IllegalAccessException,
			NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		if (apiType == null) {
			throw new IllegalArgumentException("apiType 为 null。");
		}

		if (!Module.class.isAssignableFrom(apiType)) {
			throw new IllegalArgumentException(apiType.getName() + " 不是一个 Module 类型。");
		}

		synchronized (CACHED_DATA_SERVICES_LOCK) {
			IDataService service = cachedDataServices.getOrDefault(apiType, null);
			if (service != null) {
				return service;
			}

			// 包完整名称，期待的格式为：com.{developerCode}.{appIdentifier}.{moduleIdentifier}
			String packName = apiType.getPackage().getName();
			String[] names = packName.split("\\.");

			if (names.length < 4) {
				throw new IllegalArgumentException("无法从 " + apiType.getName() + " 的包名称中解析开发者代码和 App 标识符。");
			}

			// 查找应用的配置信息
			AppConfig appConfig = PittypatConfig.getCurrent().getAppConfig(names[1], names[2]);
			if (appConfig == null) {
				throw new ConfigException(String.format("没有找到 App '%s.%s' 的配置信息。", names[1], names[2]));
			}

			// 查找模块的配置信息
			ModuleConfig moduleConfig = appConfig.getModuleConfig(names[3]);
			if (moduleConfig == null) {
				throw new ConfigException(String.format("没有找到模块 '%s.%s.%s' 的配置信息。", names[1], names[2], names[3]));
			}

			// 已经配置的数据服务类型名称
			String serviceTypeName = moduleConfig.getDataServiceType();
			if (serviceTypeName == null || serviceTypeName.length() == 0) {
                // 没有为目标模块提供数据服务类型，也就是说，目标模块不需要访问数据服务
				throw new ConfigException(String.format("没有提供模块 '%s' 的数据服务类型。", packName));
			}

			// 已经配置的数据服务路径或连接字符串
			String serviceUrl = moduleConfig.getDataServiceUrl();
			if (serviceUrl == null || serviceUrl.length() == 0) {
                // 没有为目标模块提供数据服务的路径，比如没有提供数据库连接字符串
				throw new ConfigException(String.format("没有提供模块 '%s' 的数据服务的路径。", packName));
			}

			// 解密服务路径或连接字符串
			serviceUrl = getDataServiceUrlDecryptor().decrypt(serviceUrl);

			if (serviceTypeName.equalsIgnoreCase("mysql")) {
                // 预定义的 MySQL 数据库
				service = new MySqlService(serviceUrl);
			} else if (serviceTypeName.equalsIgnoreCase("sqlserver")) {
                // 预定义的 SQL SERVER 数据库
				service = new SqlServerService(serviceUrl);
			} else {
                // 将数据服务类型字符串看作完整的类型名称，加载目标服务类型
				Class<?> type = Class.forName(serviceTypeName);

				if (type == null) {
                    // 没有找到目标类型
					throw new ClassNotFoundException(String.format("没有提供找到数据服务类型 '%s'。", packName));
				}

				Constructor<?> ctor = type.getDeclaredConstructor(String.class);
				service = (IDataService) ctor.newInstance(serviceUrl);
			}

			if (service != null) {
	            // 缓存数据服务对象
				cachedDataServices.put(apiType, service);
			}

			return service;
		}
	}

	/**
	 * 默认的服务路径解密程序实现。该实现认为服务路径没有被加密。
	 */
	private static final class DefaultDecryptor implements IDataServiceUrlDecryptor {
		/**
		 * 默认的服务路径解密程序实例。
		 */
		public static final DefaultDecryptor DEFAULT_INSTANCE = new DefaultDecryptor();

		/**
		 * 初始化 DefaultDecryptor 类的新实例。
		 */
		private DefaultDecryptor() {
		}

		/**
		 * 解密指定的数据服务的路径。
		 * 
		 * @param url
		 *            要解密的数据服务的路径。
		 * @return 解密后的数据服务的路径。
		 */
		@Override
		public String decrypt(String url) {
			return url;
		}
	}

	// 已经缓存的当前正在使用的服务路径解密程序实例。
	private static IDataServiceUrlDecryptor dataServiceUrlDecryptor = null;

	// 同步对当前解密程序的访问。
	private static final Object DATA_SERVICE_URL_DECRYPTOR_LOCK = new Object();

	// 当前正在使用的服务路径解密程序。
	private static IDataServiceUrlDecryptor getDataServiceUrlDecryptor()
			throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		if (dataServiceUrlDecryptor == null) {
			synchronized (DATA_SERVICE_URL_DECRYPTOR_LOCK) {
				if (dataServiceUrlDecryptor == null) {
					// 获取配置文件中配置的解密程序的类型名称。
					String typeName = PittypatConfig.getCurrent().getDataServiceUrlDecryptor();

					if (typeName == null || typeName.length() == 0) {
						// 没有提供配置，使用默认的解密程序。
						dataServiceUrlDecryptor = DefaultDecryptor.DEFAULT_INSTANCE;
					} else {
						// 加载解密程序的类型
						Class<?> type = Class.forName(typeName);
						if (type == null) {
							throw new ClassNotFoundException(String.format("没有找到服务路径解密程序：%s。", typeName));
						}

						// 创建解密程序的对象实例
						dataServiceUrlDecryptor = (IDataServiceUrlDecryptor) type.newInstance();
					}
				}
			}
		}

		return dataServiceUrlDecryptor;
	}
}
